1092 stories
·
0 followers

Intercepting messages inside Is­Dialog­Message, fine-tuning the message filter

1 Share

Last time, we used a MSGF_DIALOG­BOX message filter to hook into the Is­Dialog­Message so that we had the option to grab the ESC before it gets turned into an IDCANCEL. There are some problems with our initial foray.

One is the problem of recursive dialogs. If the first dialog shows another copy of itself (for example, a certificate dialog showing a dialog for its parent certificate), then the thread-local variable gets overwritten, and the first dialog’s information is lost.

We could solve that by having each dialog remember the original value and restore it when the dialog dismisses. Alternatively, we could maintain an explicit stack of dialogs, pushing when a new dialog is created and popping when it is destroyed.

However, this fails to handle the case where the dialog is modeless. In that case, the two dialogs could be running concurrently rather than recursively. Instead of a stack, we really need a per-thread set of active dialogs.

Another thing to worry about is that if this code is put into a static library, and two components in the same thread both use that static library, then you have to be careful that the two copies of the library don’t conflict with each other.

I came up with this initial idea:

#define DIALOG_WANTS_ESC_PROP TEXT("DialogWantsEsc")

LRESULT CALLBACK DialogEscHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == MSGF_DIALOGBOX) {
        auto msg = (MSG*)lParam;
        if (msg->message == WM_KEYDOWN &&
            msg->wParam == VK_ESCAPE) {
            auto hdlg = GetAncestor(msg->hwnd, GA_ROOT);
            auto customMessage = PtrToUint(GetProp(hdlg,
                                           DIALOG_WANTS_ESC_PROP));
            if (customMessage &&
                !(SendMessage(msg->hwnd, WM_GETDLGCODE,
                             msg->wParam, lParam) &
                         (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE))) {
                return SendMessage(hdlg, customMessage, 0, lParam);
            }
        }
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}

The idea here is that instead of having to manage a table of per-thread registrations, we just let dialogs self-register by setting the DIALOG_WANTS_ESC_PROP property to the message number they want to receive when the user presses ESC.

If there are two copies of this hook installed, then the Dialog­Esc­Hook­Proc is called twice. The first one sends the custom message and gets the dialog’s response, and returns it; it never passes the message down the hook chain. Therefore, the second and subsequent hooks never get to run, so we don’t have a problem of the custom message getting sent multiple times for the same call to Is­Dialog­Message.

This design has the advantage that multiple DLLs using this pattern can coexist because the first hook (whichever it is) does all the work for everybody.

An alternate, more complex, design would pass the call down the chain if the dialog box declined to handle the ESC key, in case some other hook wanted to do something special. The catch is that if there are multiple copies of this hook installed, each one will send the custom message to the dialog, which would be bad if the handler for the custom message had side effects like showing a confirmation dialog.

So we can add the rule that the custom message must be safe to call multiple times if it returns FALSE. This means that if it wants to display a confirmation dialog, it should always return TRUE even if the user cancels.

LRESULT CALLBACK DialogEscHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (code == MSGF_DIALOGBOX) {
        auto msg = (MSG*)lParam;
        if (msg->message == WM_KEYDOWN &&
            msg->wParam == VK_ESCAPE) {
            auto hdlg = GetAncestor(msg->hwnd, GA_ROOT);
            auto customMessage = PtrToUInt(GetProp(hdlg,
                                           DIALOG_WANTS_ESC_PROP));
            if (customMessage &&
                !(SendMessage(msg->hwnd, WM_GETDLGCODE,
                             msg->wParam, msg) &
                         (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE)) &&
                 SendMessage(hdlg, customMessage, 0, lParam)) {
                 return TRUE;                                  
            }
        }
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}

Or we can have the first hook leave a note for the other hooks that the message has already been handled and that they shouldn’t try to handle it again.

#define DIALOG_WANTS_ESC_PROP TEXT("DialogWantsEsc")
#define CURRENT_MESSAGE_PROP TEXT("DialogWantsEscCurrentMessage")

LRESULT CALLBACK DialogEscHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (code == MSGF_DIALOGBOX) {
        auto msg = (MSG*)lParam;
        if (msg->message == WM_KEYDOWN &&
            msg->wParam == VK_ESCAPE) {
            auto hdlg = GetAncestor(msg->hwnd, GA_ROOT);
            auto customMessage = PtrToUInt(GetProp(hdlg,
                                           DIALOG_WANTS_ESC_PROP));
            if (customMessage) {
                auto previous = GetProp(hdlg, CURRENT_MESSAGE_PROP);
                if (previous != msg &&                              
                    !(SendMessage(msg->hwnd, WM_GETDLGCODE,
                                 msg->wParam, msg) &
                             (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE))) {
                    return SendMessage(hdlg, customMessage, 0, lParam);
                }
                SetProp(hdlg, CURRENT_MESSAGE_PROP, msg);                     
                auto result = CallNextHookEx(nullptr, nCode, wParam, lParam); 
                SetProp(hdlg, CURRENT_MESSAGE_PROP, previous);                
                return result;                                                
            }
        }
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}

The first hook will send the message to the dialog. and if the dialog declines to handle it, it passes the messages to the other hooks, but setes the “current message” property to the message that was already handled, so that other hooks won’t try to handle it again.

The last part of the puzzle is installing the hook. Since we are assuming that we cannot alter the dialog loop, the hook has to be installed by the dialog itself.

Let’s assume that this dialog box already allocates other dialog state, so we can add the hook handle to the state structure.

struct DIALOGSTATE
{
    wil::unique_hhook escapeHook;
    ⟦ other stuff ⟧
};

// each dialog can choose its own custom message
#define DM_ESCPRESSED (WM_USER+1000)

INT_PTR CALLBACK DialogProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_INITDIALOG:
        {
            DIALOGSTATE* state = new(std:nothrow) DIALOGSTATE();
            if (!state) { EndDialog(hdlg, -1); return FALSE; }
            SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR)state);
            state->escapeHook.reset(SetWindowsHookEx(WM_MSGFILTER,     
                             DialogEscHookProc,                        
                             nullptr, GetCurrentThreadId()));          
            SetProp(hdlg, DIALOG_WANTS_ESC_PROP,                       
                    IntToPtr(DM_ESCPRESSED));                          
            ⟦ other dialog initialization as before ⟧
            ⟦ ending with "return (whatever)" ⟧
        }

    case DM_ESCPRESSED:
        if (⟦ we want to process the ESC key ourselves ⟧) {
            ⟦ do custom ESC key processing ⟧
            SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE);
            return TRUE;
        }
        break;

    case WM_DESTROY:
        {
            auto state = (DLGSTATE*)GetWindowLongPtr(hdlg, DWLP_USER);
            delete state;
        }
        break;

    ⟦ handle other messages ⟧
    }
    return FALSE;
}

The dialog installs the hook when it is created and removes it when it is destroyed. The hook has become an implementation detail of the dialog.

Now, I don’t recommend doing all this. Better is to just treat with the ESC like any other press of the (possibly imaginary) Cancel button. One of the few scenarios I can think of where this could be useful is if you want to display an extra confimation for the Close button (since its meaning is potentially ambiguous). This is still nonstandard, but at least it’s not too nonstandard. And for that, you can just intercept WM_CLOSE instead of trying to intercept the ESC. Intercepting the ESC was really just an excuse to show off message filters, which tend to be unappreciated.

The post Intercepting messages inside <CODE>Is­Dialog­Message</CODE>, fine-tuning the message filter appeared first on The Old New Thing.

Read the whole story
Share this story
Delete

'The Death of Spotify: Why Streaming is Minutes Away From Being Obsolete'

1 Share
An anonymous reader shares a column: I'm going to take the diplomatic hat off here and say with brutal honesty: basically everybody in the music business hates Spotify except for the people who work there. It's a platform that sucks artists for everything they have, it actively prevents community building, and, despite all of that, the platform still struggles to maintain a healthy profit margin. The streaming business model is fundamentally broken. And eventually, its demise will become more and more obvious to recognize. I'll break down exactly why the DSP era is coming to a grinding halt, why the major labels are quietly terrified, and why the artists who don't pivot now are going to go down with the ship. [...] Jimmy Iovine put it bluntly: "The streaming services have a bad situation, there's no margins, they're not making any money." This model only works for Apple, Amazon, and Google, because they don't need their music platforms to be wildly profitable. Amazon uses music as a loss-leader to keep you paying for Prime. Apple uses it to sell $1,000 iPhones. As for Spotify, or any standalone music streaming company, they're kind of screwed. And guess what -- when the platform's margins are structurally squeezed, guess who gets squeezed first? The artists. [...] What if Jimmy is right? If the DSPs are "minutes away from obsolete," what replaces them? Well, I'm not sure the DSPs are going to disappear overnight, but if you're an artist or a manager trying to sustain yourself in this evolving music economy, the answer is direct ownership. The artists who will survive the next five years are the ones who are quietly shifting their focus away from the "ATM Machine." They are building their own cultural hangars. They are capturing phone numbers on Laylo. They are driving fans to private Discord servers. They are focusing on ARPF (Average Revenue Per Fan) through high-margin merch, vinyl, and hard tickets, rather than begging for fractions of a penny from a playlist placement. We are witnessing the death of the "Mass Audience" and the birth of the "Micro-Community."

Read more of this story at Slashdot.

Read the whole story
Share this story
Delete

Your Smart TV May Be Crawling the Web for AI

1 Share
Bright Data, a company that operates one of the world's largest residential proxy networks, has been running an SDK inside smart TV apps that turns those devices into nodes for web crawling -- collecting data used by AI companies, among other clients -- and most consumers have had no idea it was happening. The company has published more than 200 first-party apps to LG's app store alone and still lists Samsung's Tizen OS and LG's webOS as supported platforms, though LG says the SDK is "not officially supported" and its operation on webOS "is not guaranteed." Google, Amazon, and Roku have all since adopted policies restricting or banning background proxy SDKs, and Bright Data no longer supports those platforms. Several Roku apps still running the SDK disappeared from the store after a journalist with The Verge behind this reporting contacted the company.

Read more of this story at Slashdot.

Read the whole story
Share this story
Delete

Chinese Official's Use of ChatGPT Revealed a Global Intimidation Opperation

1 Share
New submitter sabbede shares a report from CNN Politics: A sprawling Chinese influence operation -- accidentally revealed by a Chinese law enforcement official's use of ChatGPT -- focused on intimidating Chinese dissidents abroad, including by impersonating US immigration officials, according to a new report from ChatGPT-maker OpenAI. The Chinese law enforcement official used ChatGPT like a diary to document the alleged covert campaign of suppression, OpenAI said. In one instance, Chinese operators allegedly disguised themselves as US immigration officials to warn a US-based Chinese dissident that their public statements had supposedly broken the law, according to the ChatGPT user. In another case, they describe an effort to use forged documents from a US county court to try to get a Chinese dissident's social media account taken down. "This is what Chinese modern transnational repression looks like," Ben Nimmo, principal investigator at OpenAI, told reporters ahead of the report's release. "It's not just digital. It's not just about trolling. It's industrialized. It's about trying to hit critics of the CCP [Chinese Communist Party] with everything, everywhere, all at once." Michael Horowitz, a former Pentagon official focused on emerging technologies, said the report from OpenAI "clearly demonstrates the way that China is actively employing AI tools to enhance information operations. US-China AI competition is continuing to intensify. This competition is not just taking place at the frontier, but in how China's government is planning and implementing the day-to-day of their surveillance and information apparatus."

Read more of this story at Slashdot.

Read the whole story
Share this story
Delete

Stop using booleans, they’re hurting your code

1 Share

At Software Verify we’ve started to phase out the use of booleans in our code.

The use of booleans hinders readability and maintainability of the code.

The use of booleans also allow potential unwanted parameter reordering mistakes to occur.

Some bold claims. I will explain.

Problem 1: Readability and maintainability

If you saw this function call in your code what do you think it would do?

    ok = attachExceptionTracerToRunningProcess(targetProcessId, TRUE, FALSE, _T("d:\\Exception Tracer"));

To find out we’d need to look at the function prototype, or the documentation. Most likely you’re is going to right click in Visual Studio and ask to see the function definition, or a tooltip is going to tell you what the argument names are. 

Here’s the function definition:

    int attachExceptionTracerToRunningProcess(const DWORD  processId,
                                              const bool   processBitDepth,
                                              const bool   singleStep,
                                              const TCHAR* dir);

To use this function you need to pass a process id, a directory path and two boolean values.

We can guess that passing TRUE for singleStep will start Exception Tracer in single stepping mode. But that’s implied, it’s not explicit.

But processBitDepth, what does that do? You’ll need to read the documentation for that : FALSE starts 32 bit Exception Tracer, and TRUE starts 64 bit Exception Tracer.

Reading documentation is not a problem, but it doesn’t make your code very readable does it? It slows things down. 

Swap booleans for enumerations

If we swap the bools for typedef’d enumerations then we get something more readable for the function definition:

    typedef enum _processBitDepth
    {
        PROCESS_BIT_DEPTH_32,
        PROCESS_BIT_DEPTH_64,
    } PROCESS_BIT_DEPTH;

    typedef enum _doSingleStep
    {
        DO_SINGLE_STEP_NO,
        DO_SINGLE_STEP_YES,
    } DO_SINGLE_STEP;

    int attachExceptionTracerToRunningProcess(const DWORD             processId,
                                              const PROCESS_BIT_DEPTH processBitDepth,
                                              const DO_SINGLE_STEP    singleStep,
                                              const TCHAR*            dir);

Now the function call becomes:

    ok = attachExceptionTracerToRunningProcess(targetProcessId, PROCESS_BIT_DEPTH_64, DO_SINGLE_STEP_NO, _T("d:\\Exception Tracer"));

and the purpose of the function arguments is explicit rather than implied.

Different styles

You don’t need to just use YES and NO, you can use what ever nomenclature feels correct for the situation:

    typedef enum _doSingleStep
    {
        DO_SINGLE_STEP_NO,
        DO_SINGLE_STEP_YES,
    } DO_SINGLE_STEP;

    typedef enum _doSingleStep
    {
        DO_SINGLE_STEP_OFF,
        DO_SINGLE_STEP_ON,
    } DO_SINGLE_STEP;

    typedef enum _doSingleStep
    {
        DO_SINGLE_STEP_DISABLE,
        DO_SINGLE_STEP_ENABLE,
    } DO_SINGLE_STEP;

    typedef enum _doSingleStep
    {
        DO_SINGLE_STEP_STOP,
        DO_SINGLE_STEP_GO,
    } DO_SINGLE_STEP;

If you really feel TRUE/FALSE are appropriate you can do that as well. Which seems a bit odd, until you see Problem 2 and Problem 3.

    typedef enum _doSingleStep
    {
        DO_SINGLE_STEP_FALSE,
        DO_SINGLE_STEP_TRUE,
    } DO_SINGLE_STEP;

Problem 2: Implicit casting

If you are using bools, many types (char, int, enumerations, etc) can implicitly convert to them without you needing to use a cast statement. This can be convenient, but it’s also an avenue for bugs.

You really want to be explicit about any type conversions.

If you’re using typedef’d enumerations, these type conversions fail to compile.

Problem 3: Unwanted parameter reordering

If you are passing parameters through multiple functions before they finally reach the intended function it’s possible that a future edit (such as refactoring a function’s parameters) may reorder some parameters and a mistake is made during the updating of calls to the refactored function which results in parameters passed to the new function definition have been unintentionally reordered.

This happened to us with some boolean parameters in our code instrumentation library. We discovered the mistake when we switched to using enumerations because now each of these parameters has it’s own type.

Incorrect, but compiles

With the old function definition, this example will compile, but the parameters have been incorrectly switched, leading to incorrect behaviour.

    int attachExceptionTracerToRunningProcess(const DWORD  processId,
                                              const bool   processBitDepth,
                                              const bool   singleStep,
                                              const TCHAR* dir);

    int doStartup(const DWORD  processId,
                  const bool   processBitDepth,
                  const bool   singleStep,
                  const TCHAR* dir)
    {
        int ok;

        ok = attachExceptionTracerToRunningProcess(processId, singleStep, processBitDepth, _T("d:\\Exception Tracer"));
    }

Incorrect, but fails to compile

With the new function definition, this example will fail to compile.

    int attachExceptionTracerToRunningProcess(const DWORD             processId,
                                              const PROCESS_BIT_DEPTH processBitDepth,
                                              const DO_SINGLE_STEP    singleStep,
                                              const TCHAR*            dir);

    int doStartup(const DWORD             processId,
                  const PROCESS_BIT_DEPTH processBitDepth,
                  const DO_SINGLE_STEP    singleStep,
                  const TCHAR*            dir)
    {
        int ok;

        ok = attachExceptionTracerToRunningProcess(processId, singleStep, processBitDepth, _T("d:\\Exception Tracer"));
    }

Making the change

It’s a bit more work to create the enumeration and use it.

With an existing code base it can be time consuming work depending on the number of occurrences of the new enumerated type in your code base.

One type at a time

If you are going to make this change I would recommend changing one parameter type at a time and doing a build after each type change.

This seems more time consuming, but in practice we’ve found it’s easier than making multiple new types and then trying to build and dealing with the chaos that results until you’ve fixed up all the function definitions etc.

If, like us, you’ve got many solutions and many projects in each solution then the quickest way to do these builds is to load the solutions/projects into Visual Studio Project Builder then build all projects of a specific configuration. This saves a lot of time.

Should we change our whole codebase?

There are certainly benefits to doing this. You might find some errors where parameters have been passed in the wrong order.

But for large codebases, changing every boolean use is probably impractical (it will take a long time). The best approach is to change them when you’re editing some code that is using them.

Conclusion

As you can see there are multiple benefits to using enumerations rather than booleans in your code.

You get improved readability and improved type safety.

The post Stop using booleans, they’re hurting your code appeared first on Software Verify.

Read the whole story
Share this story
Delete

The Government Just Made it Harder to See What Spy Tech it Buys

1 Share
An anonymous reader shares a report: It might look like something from the early days of the internet, with its aggressively grey color scheme and rectangles nested inside rectangles, but FPDS.gov is one of the most important resources for keeping tabs on what powerful spying tools U.S. government agencies are buying. It includes everything from phone hacking technology, to masses of location data, to more Palantir installations. Or rather, it was an incredible tool and the basis for countless of my own investigations and others. Because on Wednesday, the government shut it down. Its replacement, another site called SAM.gov with Uncle Sam branding, frankly sucks, and makes it demonstrably harder to reliably find out what agencies, including Immigration and Customs Enforcement (ICE), are spending tax payers dollars on. "FPDS may have been a little clunky, but its simple, old-school interface made it extremely functional and robust. Every facet of government operations touches on contracting at one point, and this was the first tool that many investigative journalists and researchers would reach for to quickly find out what the government is buying and who is selling it, and how these contracts all fit together," Dave Maass, director of investigations at the Electronic Frontier Foundation, told me.

Read more of this story at Slashdot.

Read the whole story
Share this story
Delete
Next Page of Stories